﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using XISoft;
using XISoft.AppCreator.Util.Common;

namespace XISoft.AppCreator.TplEngine.Syntax
{
    /// <summary>
    /// init（） 语法分析
    /// Variable设置变量
    /// run（）语义分析并执行
    /// result返回结果
    /// </summary>
    public class ReadTpl
    {
        private readonly Encoding fileEncode;
        private readonly String content;
        private StringBuilder results = null;
        private Hashtable variables = new Hashtable();

        private const String strPatternBegin = @"<#(?<type>.*)\((?<var>.*)\)>";
        private const String strPatternEnd = @"</#(?<type>.*)>";
        private const String strPetternElse = @"<#else/>";
        private const String strPetternFor = @"(?<var>.*):(?<list>.*)";



        private CodeModel document = new CodeModel();

        /// <summary>
        /// 模板文件
        /// </summary>
        /// <param name="fileName"></param>
        public ReadTpl(String fileName)
        {
            StreamReader reader = null;
            FileStream fileStream = null;
            content = String.Empty;

            try
            {
                fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                reader = new StreamReader(fileStream);
                content = reader.ReadToEnd();
                fileEncode = EncodingType.GetType(fileStream);
            }
            catch (IOException e)
            {
                LogHelper.Trace(e);
                throw e;
            }
            finally
            {
                if (reader != null) reader.Close();
                if (fileStream != null) fileStream.Close();
            }
        }

        public String Result
        {
            get { return results.ToString(); }
        }

        public Hashtable Variables
        {
            set { variables = value; }
        }

        public void init()
        {
            const int Count = 3;
            int min, i;

            CodeModel p;

            document.children = new List<CodeModel>();
            document.parent = null;
            document.end = content.Length;
            p = document;
            try
            {

                MatchCollection[] type = new MatchCollection[Count];
                type[0] = Regex.Matches(content, strPatternBegin, RegexOptions.IgnoreCase | RegexOptions.Compiled);
                type[1] = Regex.Matches(content, strPatternEnd, RegexOptions.IgnoreCase | RegexOptions.Compiled);
                type[2] = Regex.Matches(content, strPetternElse, RegexOptions.IgnoreCase | RegexOptions.Compiled);

                #region 三路归并
                //三路: item[0],item[1],item[2]
                //队头元素:match[0],match[1],match[2]
                //归并到codeModel
                //index表示队列里是否还有元素  第一位是 item[0]  第二位是 item[1] 第三位是 item[2]
                //根据在源代码中的位置即match[min].Index
                /**
                 * while(还有没遍历的节点)
                 * {
                 *   min = 三个队头中，位置最前（index属性最小）的元素所在的队列
                 *   分析 min队头元素
                 *   将min队的头指针后移（）
                 * }
                 * */
                Match[] match = new Match[Count];
                int index = 7;
                IEnumerator[] item = new IEnumerator[Count];
                //取首元素
                for (i = 0; i < Count; i++)
                {
                    item[i] = type[i].GetEnumerator();
                    if (item[i].MoveNext())
                        match[i] = (Match)item[i].Current;
                    else
                        index &= ~(0x1 << i);
                }
                while (index > 0)
                {
                    //找最小元素
                    min = -1;
                    for (i = 0; i < Count; i++)
                    {
                        if ((index & (0x1 << i)) > 0 && (min == -1 || match[min].Index > match[i].Index))
                            min = i;
                    }

                    //取出最小元素
                    CodeModel tt = null;
                    /**
                     * p=root;
                     * if(是 if，for标志）
                     * {
                     *     tt=new CodeModel（）；
                     *     tt.parent=p;
                     *     p.children.add(tt);
                     * }else if(是endIf，endFor)
                     * {
                     *     p=p.parent；
                     * }
                     * 
                     * */
                    switch (min)
                    {
                        case 0:
                            tt = new CodeModel();
                            tt.type = change(match[min].Groups["type"].Value.Trim());
                            if (tt.type == CodeType.@for)
                            {
                                Match tmp = Regex.Matches(match[min].Groups["var"].Value.Trim(), strPetternFor, RegexOptions.IgnoreCase | RegexOptions.Compiled)[0];
                                tt.express1 = tmp.Groups["var"].Value.Trim();
                                tt.express2 = tmp.Groups["list"].Value.Trim();
                            }
                            else
                                tt.express1 = match[min].Groups["var"].Value.Trim();

                            tt.start = match[min].Index;
                            tt.headLength = match[min].Length;


                            tt.children = new List<CodeModel>();
                            tt.children1 = new List<CodeModel>();
                            tt.parent = p;

                            p.children.Add(tt);
                            p = tt;

                            break;
                        case 1:

                            CodeType ctype = change(match[min].Groups["type"].Value.Trim());
                            if (ctype != p.type) throw new Exception("if或for不成对，char" + match[min].Index);

                            if (ctype == CodeType.@for)
                            {
                                p.index1 = match[min].Index;
                                p.footLength = match[min].Length;

                            }
                            else if (ctype == CodeType.@if)
                            {
                                if (p.elseLength != default(int))//has else
                                {
                                    p.index2 = match[min].Index;
                                }
                                else
                                {
                                    p.index1 = match[min].Index;
                                }
                                p.footLength = match[min].Length;
                            }
                            p.end = match[min].Index + match[min].Length;
                            p = p.parent;
                            break;
                        case 2:
                            if (p.type != CodeType.@if || p.elseLength != default(int)) throw new Exception("多余的else");
                            p.index1 = match[min].Index;
                            p.elseLength = match[min].Length;
                            break;
                        default:
                            break;
                    }


                    //补齐
                    if (item[min].MoveNext())
                        match[min] = (Match)item[min].Current;
                    else
                        index &= ~(0x1 << min);

                }
                #endregion
                if (p != document) throw new Exception("模板出错，if或for不成对");
            }
            catch (Exception e)
            {
                LogHelper.Trace(e);
                throw e;
            }

        }

        /// <summary>
        /// 开始解析
        /// </summary>
        public void run()
        {
            //程序解释前的准备
            results = new StringBuilder();


            //开始运行
            run(document);
        }

        #region 语义分析并解释
        /* author long
         * Written in 7/30/2009
         * 
         * 步骤
         * while（遍历所有的if或for）
         * {
         *    解释if或for前面的标记（${}）
         *    解释for或if里面（递归）
         * }
         * 解释所有if或for之后的标记
         * 解释所有标记之后的内容
         * */
        private void run(CodeModel root)
        {
            Tags tag;
            String tmp;
            int start = root.start + root.headLength, temp = 0;

            //while（遍历所有的if或for）
            foreach (CodeModel cmod in root.children)
            {
                //解释if或for前面的标记（${}）
                tmp = content.Substring(start, cmod.start - start);
                temp = start;

                tag = new Tags(tmp, variables);

                while (tag.next())
                {
                    results.Append(content.Substring(start, tag.Index + temp - start));
                    start = tag.Index + tag.Length + temp - 1;
                    results.Append(tag.keyValue);
                }

                results.Append(content.Substring(start, cmod.start - start));

                start = cmod.end;

                //解释for或if里面（递归）
                if (cmod.type == CodeType.@if)
                    doIf(cmod);
                else if (cmod.type == CodeType.@for)
                    doFor(cmod);



            }

            //解释所有if或for之后的标记
            tmp = content.Substring(start, root.end - root.footLength - start);

            temp = start;
            tag = new Tags(tmp, variables);

            while (tag.next())
            {
                results.Append(content.Substring(start, tag.Index + temp - start));
                start = tag.Index + tag.Length + temp - 1;
                results.Append(tag.keyValue);
            }

            //解释所有标记之后的内容
            results.Append(content.Substring(start, root.end - root.footLength - start));
        }
        #endregion

        #region equ CodeType
        /*
         * 字符串类型的
         * */
        private CodeType change(String test)
        {
            String tmp = test.ToLower();
            if (tmp.Equals(CodeType.@if.ToString()))
                return CodeType.@if;
            else if (tmp.Equals(CodeType.@for.ToString()))
                return CodeType.@for;
            else if (tmp.Equals(CodeType.@else.ToString()))
                return CodeType.@else;
            throw new Exception("error type");
        }
        #endregion

        #region do
        private void doFor(CodeModel root)
        {
            try
            {
                IEnumerable item = Tags.getObject(variables, root.express2) as IEnumerable;
                if (item == null) throw new Exception(root.express2);
                IEnumerator itemer = item.GetEnumerator();


                while (itemer.MoveNext())
                {
                    variables[root.express1] = itemer.Current;
                    run(root);
                }
                variables.Remove(root.express1);
            }
            catch (Exception e)
            {
                LogHelper.Trace(e);
            }

        }

        private void doIf(CodeModel root)
        {
            if (root.elseLength != default(int)) 
            {//if else endif
                if (judgeIf(root.express1))
                {
                    int footlength = root.footLength;
                    int end = root.end;

                    root.footLength = root.elseLength;
                    root.end = root.index1 + root.elseLength;

                    run(root);

                    root.footLength = footlength;
                    root.end = end;
                }
                else
                {
                    List<CodeModel> tmp = root.children;
                    int headLength = root.headLength;
                    int index1 = root.index1;
                    int start = root.start;

                    root.children = root.children1;
                    root.start = root.index1;
                    root.index1 = root.index2;
                    root.headLength = root.elseLength;

                    run(root);

                    root.start = start;
                    root.index1 = index1;
                    root.headLength = headLength;
                    root.children = tmp;
                }
            }else
                if (judgeIf(root.express1))
                {
                    run(root);
                }

        }

        #endregion

        #region judgeIf
        bool judgeIf(string express1)
        {
            string formatStringAnd = @"\&\&";
            MatchCollection[] matchCollection = new MatchCollection[3];
            matchCollection[0] = Regex.Matches(express1, formatStringAnd, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            string formatStringOr = @"\|\|";
            matchCollection[1] = Regex.Matches(express1, formatStringOr, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            //Console.WriteLine(matchCollection[0].Count);
            //Console.WriteLine(matchCollection[1].Count);
            if (matchCollection[0].Count + matchCollection[1].Count > 1)
                Console.WriteLine("express1 error!");
            else if (matchCollection[0].Count + matchCollection[1].Count == 0)
                return judge(express1);
            else if (matchCollection[0].Count + matchCollection[1].Count == 1)
            {
                string var1 = String.Empty;
                string var2 = String.Empty;
                if (matchCollection[0].Count == 1)
                {
                    Console.WriteLine(matchCollection[0][0].Index);
                    var1 = express1.Substring(0, matchCollection[0][0].Index);
                    var2 = express1.Substring(matchCollection[0][0].Index + 2, express1.Length - 2 - matchCollection[0][0].Index);
                    return (judge(var1) && judge(var2));
                }
                else if (matchCollection[1].Count == 1)
                {
                    Console.WriteLine(matchCollection[1][0].Index);
                    var1 = express1.Substring(0, matchCollection[1][0].Index);
                    var2 = express1.Substring(matchCollection[1][0].Index + 2, express1.Length - 2 - matchCollection[1][0].Index);
                    return (judge(var1) || judge(var2));
                }
            }
            return false;
        }

        #endregion

        #region judge
        bool judge(string strContent)
        {
            string[] str = new string[3];
            int result;

            string formatString = @"\=\=";
            MatchCollection[] matchCollection = new MatchCollection[6];
            matchCollection[0] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            formatString = @"/>/=";
            matchCollection[1] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            formatString = @"/<\=";
            matchCollection[2] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            formatString = @"/<";
            matchCollection[3] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            formatString = @"/>";
            matchCollection[4] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            formatString = @"!\=";
            matchCollection[5] = Regex.Matches(strContent, formatString, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            if (matchCollection[0].Count == 1)
            {
                str[1] = @"\=\=";
                str[0] = strContent.Substring(0, matchCollection[0][0].Index);
                str[2] = strContent.Substring(matchCollection[0][0].Index + 2, strContent.Length - matchCollection[0][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result == 0)
                    return true;
                else
                    return false;
            }
            else if (matchCollection[1].Count == 1)
            {
                str[1] = @"/>/=";
                str[0] = strContent.Substring(0, matchCollection[1][0].Index);
                str[2] = strContent.Substring(matchCollection[1][0].Index + 2, strContent.Length - matchCollection[1][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result >= 0)
                    return true;
                else
                    return false;
            }
            else if (matchCollection[2].Count == 1)
            {
                str[1] = @"/<\=";
                str[0] = strContent.Substring(0, matchCollection[2][0].Index);
                str[2] = strContent.Substring(matchCollection[2][0].Index + 2, strContent.Length - matchCollection[2][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result <= 0)
                    return true;
                else
                    return false;
            }
            else if (matchCollection[3].Count == 1)
            {
                str[1] = @"/<";
                str[0] = strContent.Substring(0, matchCollection[3][0].Index);
                str[2] = strContent.Substring(matchCollection[3][0].Index + 2, strContent.Length - matchCollection[3][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result < 0)
                    return true;
                else
                    return false;
            }
            else if (matchCollection[4].Count == 1)
            {
                str[1] = @"/>";
                str[0] = strContent.Substring(0, matchCollection[4][0].Index);
                str[2] = strContent.Substring(matchCollection[4][0].Index + 2, strContent.Length - matchCollection[4][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result > 0)
                    return true;
                else
                    return false;
            }
            else if (matchCollection[5].Count == 1)
            {
                str[1] = @"/!\=";
                str[0] = strContent.Substring(0, matchCollection[5][0].Index);
                str[2] = strContent.Substring(matchCollection[5][0].Index + 2, strContent.Length - matchCollection[5][0].Index - 2);
                result = Compare(str[0], str[2]);
                if (result == 0)
                    return false;
                else
                    return true;
            }
            else
                return false;
        }

        private int Compare(String str1, String str2)
        {
            Tags tag1 = new Tags(str1, variables);
            Tags tag2 = new Tags(str2, variables);

            String result1 = String.Empty;
            if (tag1.next())
            {
                if (tag1.keyValue == null)
                    result1 = "null";
                else
                    result1 = tag1.keyValue.ToString().ToLower().Trim();
            }
            else
                result1 = str1.ToLower().Trim();

            if (tag2.next())
            {
                if (tag2.keyValue == null)
                    return String.Compare(result1, "null");
                else
                    return String.Compare(result1, tag2.keyValue.ToString().ToLower().Trim());
            }
            else
                return String.Compare(result1, str2.ToLower().Trim());
        }
        #endregion


        public void WriteFile(string szPath)
        {
            if (File.Exists(szPath))
                File.Delete(szPath);

            Directory.CreateDirectory(Directory.GetParent(szPath).FullName);

            StreamWriter streamWrite = null;
            try
            {
                streamWrite = new StreamWriter(szPath, false, fileEncode);
                streamWrite.Write(Result);
            }
            catch (Exception e)
            {
                LogHelper.Trace(e);
            }
            finally
            {
                if (streamWrite != null)
                {
                    streamWrite.Flush();
                    streamWrite.Close();
                }
            }
        }
    }
}
